import { iAspect, iPointCut, iPointCutOption } from "../../type/aoptype"
import { Util } from "../../src/util";

export class aop {

    //这是存储当前已有的aop切面的列表
    private static aspectList: Map<unknown, iAspect> = new Map();

    //这是存储当前已有的aop切点的列表
    private static pointcutList: Map<unknown, iPointCut> = new Map();

    /**
     * 
     * @param target 传进来的类
     * @returns 添加切面
     */
    public static addAspect(target: any) {
        //target 是传进来的类
        let isHas = this.aspectList.has(target)
        if (isHas) {
            //存在切面时 不需要添加
            return;
        }
        let temp: iAspect = {
            clazz: target,
            aspectName: target,
            pointcutArray: [],
        }
        this.aspectList.set(target, temp)
        //console.log(this.aspectList);
    }

    /**
     * 
     * @param cfg 切点
     * 
     * 添加切点 先判断是否存在它的切面
     */
    public static addPointCut(cfg: iPointCutOption) {
        console.log("11", cfg);
        let isHas = this.aspectList.has(cfg.clazz)
        console.log("22", isHas);

        if (!isHas) {
            this.addAspect(cfg.clazz)
            console.log("33", this.aspectList);
            //this.pointcutList.set(cfg.clazz,)
        }
        this.addPointCutToList(cfg)
    }

    /**
     * addPointCuttoList 往切点列表中加入新切点
     */
    public static addPointCutToList(cfg: iPointCutOption) {
        let currentClassNameAspect = this.aspectList.get(cfg.clazz)
        let temp = {
            clazz: cfg.clazz,
            expressions: cfg.expressions,
            pointCutName: cfg.pointCutName,
        }
        let p = this.expressionToRegExp(temp)
        currentClassNameAspect.pointcutArray.push(p)
        this.pointcutList.set(cfg.clazz, p)

    }

    /**
     * 
     * @param cfg 切点
     * @returns 转换过expressions的切点
     */
    public static expressionToRegExp(cfg: iPointCutOption): iPointCut {
        let temp: iPointCut = {
            clazz: cfg.clazz,
            expressions: [],
            pointCutName: cfg.pointCutName,
            adviceArray: cfg.adviceArray
        }
        for (let i = 0; i < cfg.expressions.length; i++) {
            temp.expressions[i] = Util.toReg(cfg.expressions[i])
        }
        return temp
    }

    /**
     * @param target 类
     * @param propertyKey 切点名
     * @param advices 通知类型
     * @returns 无
     */
    public static addAdvice(target: unknown, propertyKey: String, advices: Function[]) {
        console.log("123", target, propertyKey, advices);
        if (!this.aspectList.has(target) && !this.pointcutList.has(target)) {
            return;
        }
        let currentClassNamePointcut = this.pointcutList.get(target)
        currentClassNamePointcut.adviceArray = [...advices]
        this.proxyPointcut()
    }

    /**
     * 
     * @returns 设置代理
     */
    public static proxyPointcut() {
        if (!this.pointcutList || this.pointcutList.size === 0) {
            return
        }
        this.aspectList.forEach((value, key) => {
            if (this.pointcutList.has(key)) {
                this.proxyPointcutOne(value.clazz)
            }
        })
    }

    /**
     * 
     * @param clazz 传进来要设置代理的切点的类
     */
    public static proxyPointcutOne(clazz: any) {
        let a: iPointCut
        for (a of this.pointcutList.values()) {
            console.log("aaa", a);
            for (let temp of a.expressions) {
                let tag: RegExp = new RegExp(temp)
                Object.getOwnPropertyNames(clazz.prototype).forEach(key => {
                    if (key === 'constructor' || typeof clazz.prototype[key] !== 'function') {
                        return
                    }
                    const name = clazz.name + '.' + key;    //生成一个全名，由类的名字和属性名组成
                    if (tag.test(name)) {
                        clazz.prototype[key] = this.invoke(this.pointcutList.get(clazz), key)
                    }
                });
            }
        }
    }

    /**
     * 
     * @param pointCut 传入要代理的类
     * @param methodName  要代理的方法名
     * @returns 返回一个动态代理对象
     */
    public static invoke(pointCut: iPointCut, methodName: string) {
        console.log(Object.getOwnPropertyNames(pointCut.clazz.prototype), methodName);
        //获取当前
        const func = pointCut.clazz.prototype[methodName];
        let result: unknown
        if (!func) {
            return;
        } else {
            return function (...args) {
                let params = args
                console.log("11", params);
                //执行前置通知
                pointCut.adviceArray[0]();
                try {
                    result = func(params)
                    pointCut.adviceArray[2]();
                } catch (error) {
                    pointCut.adviceArray[3]();
                    throw error;
                }
                pointCut.adviceArray[1]();
            }
        }
        return result;
    }
}